<?php

namespace App\Common\Libs;


use App\Consts\CacheKeyConst;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

/**
 * 发送钉钉消息通知类
 * Class DingTalk
 */
class DingTalk
{

    /** @var int token expired time */
    const EXPIRED_MINUTE = 100;

    /** @var string access token url */
    const OPEN_URL_ACCESS_TOKEN = 'https://oapi.dingtalk.com/gettoken';

    /** @var string get user by mobile */
    const OPEN_URL_USER_MOBILE = 'https://oapi.dingtalk.com/user/get_by_mobile';

    /** @var string send work message */
    const OPEN_URL_WORK_MSG = 'https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2';

    /**
     * @var $_config
     */
    private $_config;

    /**
     * @var $_accessToken
     */
    private $_accessToken;

    /**
     * @var $_request
     */
    private $_request;

    /**
     * @var $_instance
     */
    private static $_instance;

    /**
     * DingTalk constructor.
     * @param $config
     */
    public function __construct($config = [])
    {
        $this->_config = array_merge(config('ding.notice'), $config);
        $this->_accessToken = $this->getAccessToken();
    }

    /**
     * 获取基本配置
     * @return mixed
     */
    public function config()
    {
        return $this->_config;
    }

    /**
     * 获取请求基本信息
     * @return mixed
     */
    public function request()
    {
        return $this->_request;
    }

    /**
     * 获取 access token
     * @return mixed|string
     */
    public function getAccessToken()
    {
        // 先检查是否有缓存
        $key = getCacheKey(CacheKeyConst::DINGTALK_ACCESS_TOKEN, [$this->_config['app_key']]);
        $accessToken = Cache::get($key);
        if (!empty($accessToken)) {
            return  $accessToken;
        }
        // 没有缓存重新请求并设置缓存
        $url = self::OPEN_URL_ACCESS_TOKEN . "?appkey={$this->_config['app_key']}&appsecret={$this->_config['app_secret']}";
        $req = self::get($url);
        $res = json_decode($req, true) ? : [];
        if ($res && 0 == $res['errcode']) {
            $accessToken = $res['access_token'];
        }
        if ($accessToken ) {
            Cache::put($key, $accessToken, self::EXPIRED_MINUTE);
        } else {
            $this->failed('ACCESS_TOKEN', $url, [], $req);
        }
        return $accessToken;
    }

    /**
     * 记录失败日志
     * @param $type
     * @param $url
     * @param $data
     * @param $req
     * @return bool
     */
    protected function failed($type, $url, $data, $req)
    {
        $info = "[DINGTALK][FAILED][{$type}] " . " url - $url - data - " . json_encode($data)
            . ' - result - ' . json_encode($req) . PHP_EOL;
        Log::info($info);
        return true;
    }

    /**
     * 获取用户ID
     * @param $mobile
     * @return mixed|string
     */
    public function getUserId($mobile)
    {
        $url = self::OPEN_URL_USER_MOBILE . "?access_token={$this->_accessToken}&mobile=$mobile";
        $res = self::get($url);
        $res = json_decode($res, true) ? : [];
        if ($res && 0 == $res['errcode']) {
            return $res['userid'];
        }
        return '';
    }

    /**
     * 发送钉钉消息
     * @param $msg
     * @param string $mobile
     * @param string $userId
     * @return mixed
     */
    public function send($msg, $mobile = '', $userId = '')
    {
        $url = self::OPEN_URL_WORK_MSG . "?access_token={$this->_accessToken}";
        $body = [
            'agent_id' => $this->_config['agent_id'],
            'userid_list' => $userId ? : $this->getUserId($mobile),
            'msg' => json_encode($msg)
        ];
        $header = [' Content-Type: application/json'];
        $res = self::post($url, $body, $header);
        $this->_request = ['url' => $url, 'method' => 'POST', 'body' => $body];
        // 保留现场原始数据，将结果留给下一层处理
        // @example: {"errcode":0,"errmsg":"ok","task_id":375166540825,"request_id":"ebjoueh4xrly"}
        return $res;
        //// 结果判断示例
        //$res = json_decode($res, true) ? : [];
        //if ($res && 0 == $res['errcode']) { return true; } // 成功
        //return false; // 失败
    }

    /**
     * 发送文本消息
     * @param $msg
     * @param $mobile
     * @return bool
     */
    public function text($msg, $mobile)
    {
        $url = self::OPEN_URL_WORK_MSG . "?access_token={$this->_accessToken}";
        $body = [
            'agent_id' => $this->_config['agent_id'],
            'userid_list' => $this->getUserId($mobile),
            'msg' => json_encode([
                'msgtype' => 'text',
                'text' => [
                    'content' => $msg['content'],
                ],
            ])
        ];
        $header = [' Content-Type: application/json'];
        $res = self::post($url, $body, $header);
        return $res;
    }

    /**
     * 发送 Markdown 消息
     * @param $msg
     * @param $mobile
     * @return bool
     */
    public function markdown($msg, $mobile)
    {
        $url = self::OPEN_URL_WORK_MSG . "?access_token={$this->_accessToken}";
        $body = [
            'agent_id' => $this->_config['agent_id'],
            'userid_list' => $this->getUserId($mobile),
            'msg' => json_encode([
                'msgtype' => 'markdown',
                'markdown' => [
                    'title' => $msg['title'],
                    'text' => $msg['content'],
                ],
            ])
        ];
        $header = [' Content-Type: application/json'];
        $res = self::post($url, $body, $header);
        return $res;
    }

    /**
     * get方式请求
     * @param $url
     * @param array $header
     * @param int $time_out
     * @return mixed
     */
    public static function get($url, array $header = [], $time_out = 10)
    {
        return self::curlRequest($url, false, [], $header, $time_out);
    }

    /**
     * post方式请求
     * @param $url
     * @param array $post_data
     * @param array $header
     * @param int $time_out
     * @return mixed
     */
    public static function post($url, array $post_data = [], array $header = [], $time_out = 10)
    {

        return self::curlRequest($url, true, $post_data, $header, $time_out);
    }

    /**
     * curl 请求
     * @param $url
     * @param bool $is_post
     * @param array $params
     * @param bool $header
     * @param int $time_out
     * @return mixed
     */
    private static function curlRequest($url, $is_post = false, $params = [], $header = false, $time_out = 60)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLINFO_HEADER_OUT, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $time_out);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $time_out);
        curl_setopt($ch, CURLOPT_POST, $is_post);
        curl_setopt($ch, CURLOPT_FAILONERROR, false);
        if (1 == strpos('$' . $url, "https://")) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }
        if ($is_post) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
        }
        if ($header) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        }
        $response = curl_exec($ch);
        curl_close($ch);
        return $response;
    }

    /**
     * @param array $config
     * @return DingTalk
     */
    public static function getInstance($config = [])
    {
        if (self::$_instance instanceof self) {
            return self::$_instance;
        } else {
            return new self($config);
        }
    }

}
